package class

import (
	"log"
)

// Object 修改 Object, 而不是 Slot 是因为, java 中数组的类型 `java.lang.Class` 也派生自 java.lang.Object
type Object struct {
	class  *Class
	data   interface{} // fields map[string]interface{}  // array  []interface{}
	parent *Object     // 父类的对象
	extra  interface{}
}

func NewObject(clazz *Class) *Object {
	if clazz == nil {
		panic("java.lang.NullPointerException")
	}
	return newObjectWithParent(clazz, make(map[string]interface{}, 255))
}

func newObjectWithParent(clazz *Class, data interface{}) *Object {
	if clazz == nil {
		return nil
	}
	fields := make(map[string]interface{}, 255)
	obj := &Object{
		class:  clazz,
		data:   data,
		parent: newObjectWithParent(clazz.SuperClass(), fields),
	}

	for _, f := range clazz.Fields() {
		if !f.IsStatic() { // && !f.IsFinal() , final == nil
			desc := clazz.ClassLoader().DefineClass(f.TypeName())
			obj.SetFields(f.Name(), desc.DefValue())
			log.Default().Printf("Init object field %v.%v = %v(%T)%v",
				f.Class().Name(), f.Name(), desc.DefValue(), desc.DefValue(), f.Descriptor())
		}
	}

	return obj
}

func zero() {

}

func NewArray(clazz *Class, count int32) *Object {
	if !clazz.IsArray() {
		panic(" Not array class: " + clazz.Name())
	}
	switch clazz.Name() {
	case "[Z":
		return newObjectWithParent(clazz, make([]int8, count))
	case "[B":
		return newObjectWithParent(clazz, make([]int8, count))
	case "[C":
		return newObjectWithParent(clazz, make([]uint16, count))
	case "[S":
		return newObjectWithParent(clazz, make([]int16, count))
	case "[I":
		return newObjectWithParent(clazz, make([]int32, count))
	case "[J":
		return newObjectWithParent(clazz, make([]int64, count))
	case "[F":
		return newObjectWithParent(clazz, make([]float32, count))
	case "[D":
		return newObjectWithParent(clazz, make([]float64, count))
	default: // [Lx.x.X; 对象的数组, [[D 多维数组
		return newObjectWithParent(clazz, make([]*Object, count))
	}

}

func (o *Object) fields() map[string]interface{} {
	return o.data.(map[string]interface{})
}

func (o *Object) Parent() *Object {
	return o.parent
}

func (o *Object) GetParent(clz *Class) (*Object, bool) {
	if clz.IsInterface() {
		panic("Error object is not interface")
	}
	for p, c := o, o.class; c != nil; p, c = p.parent, c.SuperClass() {
		if c == clz {
			if p.class != c {
				panic("!!! new object error !!!")
			}
			return p, true
		}
	}
	return nil, false
}
func (o *Object) LookupMethod(method *Method) (*Object, bool) {
	return o.lookup(&method.Member)
}

func (o *Object) lookup(method *Member) (*Object, bool) {
	ref := o
	if ref.Class() != method.Class() {
		return ref.GetParent(method.Class())
	}
	return ref, true
}

func (o *Object) LookupFiled(field *Field) (*Object, bool) {
	return o.lookup(&field.Member)
}

func (o *Object) SetData(val interface{}) {
	o.data = val
}

func (o *Object) GetField(name string) interface{} {
	for f := o; f != nil; f = f.parent {
		e, ok := o.fields()[name]
		if ok {
			return e
		}
	}
	panic("Not get field: " + name)
}

func (o *Object) Field(field *Field) interface{} {
	ref, ok := o.LookupFiled(field)
	if ok {
		v, ok := ref.fields()[field.Name()]
		if ok {
			return v
		}
	}
	panic("Not get field: " + field.Class().Name() + "." + field.Name() + " in " + o.class.Name())
}

func (o *Object) SetFields(name string, val interface{}) {
	o.fields()[name] = val
}

func (o *Object) Class() *Class {
	return o.class
}

func (o *Object) InstanceOf(clazz *Class) bool {
	return o.class.IsAssignableFrom(clazz)
}

func (o *Object) IsArray() bool {
	return o.class.IsArray()
}

func (o *Object) Bytes() []int8 {
	return o.data.([]int8)
}

func (o *Object) Shorts() []int16 {
	return o.data.([]int16)
}

func (o *Object) Ints() []int32 {
	return o.data.([]int32)
}

func (o *Object) Longs() []int64 {
	return o.data.([]int64)
}

func (o *Object) Chars() []uint16 {
	return o.data.([]uint16)
}

func (o *Object) Floats() []float32 {
	return o.data.([]float32)
}

func (o *Object) Doubles() []float64 {
	return o.data.([]float64)
}

func (o *Object) Refs() []*Object {
	return o.data.([]*Object)
}

func (o *Object) Data() interface{} {
	return o.data
}

func (o *Object) ArrayLength() int32 {
	switch o.data.(type) {
	case []int8:
		return int32(len(o.data.([]int8)))
	case []int16:
		return int32(len(o.data.([]int16)))
	case []int32:
		return int32(len(o.data.([]int32)))
	case []int64:
		return int32(len(o.data.([]int64)))
	case []uint16:
		return int32(len(o.data.([]uint16)))
	case []float32:
		return int32(len(o.data.([]float32)))
	case []float64:
		return int32(len(o.data.([]float64)))
	case []*Object:
		return int32(len(o.data.([]*Object)))
	default:
		panic("Not array")
	}
}
